package com.example.shutdown_v1.service;

import com.example.shutdown_v1.dto.SensorDataPacket;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.data.domain.Range;
import org.springframework.data.redis.connection.stream.*;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Redis传感器数据存储服务
 * 使用Redis Stream存储3分钟热数据，自动过期
 */
@Slf4j
@Service
@RequiredArgsConstructor
@ConditionalOnProperty(prefix = "data.storage.redis", name = "enabled", havingValue = "true")
public class RedisSensorDataService {

    private final RedisTemplate<String, Object> redisTemplate;

    @Value("${data.storage.redis.ttl-minutes:3}")
    private int ttlMinutes;

    /**
     * 保存传感器实时状态到Redis Hash
     * @param sensorId 传感器ID
     * @param state 状态（open/closed）
     * @param compositeAngle 综合角度
     * @param confidence 置信度
     * @param packet 传感器数据包（包含欧拉角和加速度）
     */
    public void saveSensorStatus(String sensorId, String state, Double compositeAngle, Double confidence, SensorDataPacket packet) {
        try {
            String statusKey = String.format("sensor:%s:status", sensorId);
            Map<String, Object> statusMap = new HashMap<>();
            statusMap.put("state", state);
            statusMap.put("compositeAngle", compositeAngle);
            statusMap.put("confidence", confidence);
            statusMap.put("lastUpdateTime", System.currentTimeMillis());
            statusMap.put("isOnline", true);
            
            // 保存欧拉角（用于"设置关闭状态"功能）
            if (packet != null) {
                if (packet.getRoll() != null) statusMap.put("roll", packet.getRoll());
                if (packet.getPitch() != null) statusMap.put("pitch", packet.getPitch());
                if (packet.getYaw() != null) statusMap.put("yaw", packet.getYaw());
                
                // 保存圈数数据（用于"设置关闭状态"功能和前端显示）
                if (packet.getRollTurnCount() != null) statusMap.put("rollTurnCount", packet.getRollTurnCount());
                if (packet.getYawTurnCount() != null) statusMap.put("yawTurnCount", packet.getYawTurnCount());
                if (packet.getCompositeRotationCount() != null) {
                    statusMap.put("compositeRotationCount", packet.getCompositeRotationCount());
                }
                
                // 保存加速度（用于"设置关闭状态"功能）
                if (packet.getAccelX() != null) statusMap.put("accelX", packet.getAccelX());
                if (packet.getAccelY() != null) statusMap.put("accelY", packet.getAccelY());
                if (packet.getAccelZ() != null) statusMap.put("accelZ", packet.getAccelZ());
                
                // 保存信号强度RSSI（用于前端显示）
                if (packet.getRssi() != null) statusMap.put("rssi", packet.getRssi());
            }
            
            redisTemplate.opsForHash().putAll(statusKey, statusMap);
            // 设置3分钟过期
            redisTemplate.expire(statusKey, Duration.ofMinutes(ttlMinutes));
            
            log.debug("[Redis] 保存状态: {}, 状态={}, 角度={}", sensorId, state, compositeAngle);
        } catch (Exception e) {
            log.error("[Redis] 保存状态失败: {}", sensorId, e);
        }
    }
    
    /**
     * 获取传感器实时状态
     * @param sensorId 传感器ID
     * @return 状态Map（state, compositeAngle, confidence, lastUpdateTime, isOnline）
     */
    public Map<String, Object> getSensorStatus(String sensorId) {
        try {
            String statusKey = String.format("sensor:%s:status", sensorId);
            Map<Object, Object> rawMap = redisTemplate.opsForHash().entries(statusKey);
            
            if (rawMap.isEmpty()) {
                // Redis中没有数据，说明传感器离线或超过3分钟没数据
                Map<String, Object> offlineStatus = new HashMap<>();
                offlineStatus.put("state", "unknown");
                offlineStatus.put("isOnline", false);
                return offlineStatus;
            }
            
            Map<String, Object> statusMap = new HashMap<>();
            rawMap.forEach((k, v) -> statusMap.put(k.toString(), v));
            return statusMap;
        } catch (Exception e) {
            log.error("[Redis] 获取状态失败: {}", sensorId, e);
            return new HashMap<>();
        }
    }
    
    /**
     * 保存传感器数据到Redis Stream
     * @param packet 传感器数据包
     */
    public void saveSensorData(SensorDataPacket packet) {
        try {
            String streamKey = buildStreamKey(packet.getMac());
            
            // 构建Stream数据（Map格式）
            Map<String, Object> dataMap = new HashMap<>();
            dataMap.put("sensorId", packet.getMac());
            dataMap.put("pitch", packet.getPitch());
            dataMap.put("roll", packet.getRoll());
            dataMap.put("yaw", packet.getYaw());
            dataMap.put("accelX", packet.getAccelX());
            dataMap.put("accelY", packet.getAccelY());
            dataMap.put("accelZ", packet.getAccelZ());
            dataMap.put("gyroX", packet.getGyroX());
            dataMap.put("gyroY", packet.getGyroY());
            dataMap.put("gyroZ", packet.getGyroZ());
            dataMap.put("isRotating", packet.getIsRotating());
            dataMap.put("timestamp", System.currentTimeMillis());
            
            // 写入Stream
            RecordId recordId = redisTemplate.opsForStream()
                    .add(StreamRecords.newRecord()
                            .ofObject(dataMap)
                            .withStreamKey(streamKey));
            
            // 设置Stream的过期时间（3分钟）
            redisTemplate.expire(streamKey, Duration.ofMinutes(ttlMinutes));
            
            log.debug("[Redis] 保存数据到Stream: {}, RecordId: {}, TTL: {}分钟", streamKey, recordId, ttlMinutes);
            
            // 【优化】限制Stream长度，防止内存溢出
            // 3分钟 × 6条/分钟 = 18条（网关10秒上报一次）
            redisTemplate.opsForStream().trim(streamKey, 18);
            
        } catch (Exception e) {
            log.error("[Redis] 保存数据失败: {}", packet.getMac(), e);
        }
    }

    /**
     * 查询最近N条数据
     * @param sensorId 传感器ID
     * @param limit 限制条数
     * @return 数据列表（按时间倒序）
     */
    public List<SensorDataPacket> getLatestData(String sensorId, int limit) {
        try {
            String streamKey = buildStreamKey(sensorId);
            
            // 使用 XREVRANGE 反向查询（最新的在前）
            List<MapRecord<String, Object, Object>> records = redisTemplate.opsForStream()
                    .reverseRange(streamKey, Range.unbounded(), 
                            org.springframework.data.redis.connection.Limit.limit().count(limit));
            
            if (records == null || records.isEmpty()) {
                return List.of();
            }
            
            return records.stream()
                    .map(this::convertToPacket)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());
                    
        } catch (Exception e) {
            log.error("[Redis] 查询数据失败: {}", sensorId, e);
            return List.of();
        }
    }

    /**
     * 查询指定时间范围的数据
     * @param sensorId 传感器ID
     * @param startTime 开始时间（毫秒时间戳）
     * @param endTime 结束时间（毫秒时间戳）
     * @return 数据列表
     */
    public List<SensorDataPacket> getDataByTimeRange(String sensorId, long startTime, long endTime) {
        try {
            String streamKey = buildStreamKey(sensorId);
            
            // 构建时间范围查询
            Range<String> range = Range.closed(
                    String.valueOf(startTime) + "-0",
                    String.valueOf(endTime) + "-0"
            );
            
            List<MapRecord<String, Object, Object>> records = redisTemplate.opsForStream()
                    .range(streamKey, range);
            
            if (records == null) {
                return List.of();
            }
            
            return records.stream()
                    .map(this::convertToPacket)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());
                    
        } catch (Exception e) {
            log.error("[Redis] 范围查询失败: {}", sensorId, e);
            return List.of();
        }
    }

    /**
     * 获取当前传感器数据（最新一条）
     */
    public SensorDataPacket getCurrentData(String sensorId) {
        var dataList = getLatestData(sensorId, 1);
        return dataList.isEmpty() ? null : dataList.get(0);
    }

    /**
     * 删除传感器所有数据
     */
    public void deleteAllData(String sensorId) {
        try {
            String streamKey = buildStreamKey(sensorId);
            redisTemplate.delete(streamKey);
            log.info("[Redis] 删除传感器Stream: {}", streamKey);
        } catch (Exception e) {
            log.error("[Redis] 删除失败: {}", sensorId, e);
        }
    }

    /**
     * 获取Stream中的数据条数
     */
    public long getDataCount(String sensorId) {
        try {
            String streamKey = buildStreamKey(sensorId);
            Long size = redisTemplate.opsForStream().size(streamKey);
            return size != null ? size : 0;
        } catch (Exception e) {
            log.error("[Redis] 获取数量失败: {}", sensorId, e);
            return 0;
        }
    }

    /**
     * 构建Stream Key
     * 格式: sensor:{sensorId}:stream
     */
    private String buildStreamKey(String sensorId) {
        return String.format("sensor:%s:stream", sensorId);
    }

    /**
     * 转换MapRecord到SensorDataPacket
     */
    private SensorDataPacket convertToPacket(MapRecord<String, Object, Object> record) {
        try {
            Map<Object, Object> value = record.getValue();
            
            SensorDataPacket packet = new SensorDataPacket();
            packet.setMac(getString(value, "sensorId"));
            packet.setPitch(getDouble(value, "pitch"));
            packet.setRoll(getDouble(value, "roll"));
            packet.setYaw(getDouble(value, "yaw"));
            packet.setAccelX(getDouble(value, "accelX"));
            packet.setAccelY(getDouble(value, "accelY"));
            packet.setAccelZ(getDouble(value, "accelZ"));
            packet.setGyroX(getDouble(value, "gyroX"));
            packet.setGyroY(getDouble(value, "gyroY"));
            packet.setGyroZ(getDouble(value, "gyroZ"));
            packet.setIsRotating(getBoolean(value, "isRotating"));
            
            return packet;
        } catch (Exception e) {
            log.error("[Redis] 数据转换失败", e);
            return null;
        }
    }

    private String getString(Map<Object, Object> map, String key) {
        Object value = map.get(key);
        return value != null ? value.toString() : null;
    }

    private Double getDouble(Map<Object, Object> map, String key) {
        Object value = map.get(key);
        if (value == null) return null;
        if (value instanceof Number) {
            return ((Number) value).doubleValue();
        }
        try {
            return Double.parseDouble(value.toString());
        } catch (Exception e) {
            return null;
        }
    }

    private Boolean getBoolean(Map<Object, Object> map, String key) {
        Object value = map.get(key);
        if (value == null) return null;
        if (value instanceof Boolean) {
            return (Boolean) value;
        }
        return Boolean.parseBoolean(value.toString());
    }
}

